package com.zm.spring.demo;

import com.zm.spring.demo.mapper.UserMapper;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLDecoder;
import java.sql.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MyAnnotaltionConfigApplicationContext {
    Map<String,BeanDefinitoin> beanDefinitoinMap = new HashMap<>();
    Map<String,Object> singletonObject = new HashMap<>();

    MyAnnotaltionConfigApplicationContext(Class configuration) throws UnsupportedEncodingException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        //构建beanDefinition 对象 存入beanDefinitoinMap
        //1 解析 bean 方法
        Method[] methods = configuration.getDeclaredMethods();
        Object configurationObject = configuration.newInstance();
        for (Method method : methods) {
            if (method.getAnnotation(Bean.class)!=null) {
                BeanDefinitoin bd = new BeanDefinitoin();
                bd.setBeanMethod(method);
                bd.setBeanConfig(configurationObject);
                bd.setBeanClass(method.getReturnType());
                bd.setLazy(method.getAnnotation(Lazy.class)!=null);
                bd.setPrimary(method.getAnnotation(Primary.class)!=null);
                //获取 bean 的名字
                Bean beanAnno = method.getAnnotation(Bean.class);
                String beanName;
                if (beanAnno.value().length>0) {
                    beanName = beanAnno.value()[0];
                }else {
                    beanName = method.getName();
                }
                beanDefinitoinMap.put(beanName,bd);
            }
        }

        //2 扫描 bean
        ComponentScan ComponentScan = (ComponentScan)configuration.getAnnotation(ComponentScan.class);
        if (ComponentScan!=null){
            List<File> classFiles = new ArrayList<>();
            for (String pkg : ComponentScan.value()) {
                System.out.println(pkg);
                //使用类加载器读取编译目录
                // 将class文件路径转换为类路径加载类对象，读取类上的注解，判断是否是组件bean
                String pkgPath = pkg.replaceAll("\\.","/");
                URL url = getClass().getClassLoader().getResource(pkgPath);
                System.out.println("url = " + url);
                pkgPath = URLDecoder.decode(url.getPath(),"utf-8");
                System.out.println("pkgPath = " + pkgPath);
                File pkgDir = new File(pkgPath);
                scanClassFile(pkgDir,classFiles);
            }
            String rootPath = getClass().getClassLoader().getResource("").getPath();
            System.out.println("rootPath = " + rootPath);
            for (File classFile : classFiles){
                rootPath = new File(rootPath).getAbsolutePath();
                String classPath = classFile.getAbsolutePath().replace(rootPath+"\\","");
                classPath = classPath.replace(".class","");
                classPath = classPath.replace("\\",".");
                System.out.println("classPath = " + classPath);
                System.out.println("classFile = " + classFile);
                Class<?> aClass = Class.forName(classPath);

                Service service = aClass.getAnnotation(Service.class);
                Controller controller = aClass.getAnnotation(Controller.class);
                Repository repository = aClass.getAnnotation(Repository.class);
                Component component = aClass.getAnnotation(Component.class);
                if (service!=null||
                        controller!=null||
                        repository!=null||
                        component!=null) {
                    BeanDefinitoin bd = new BeanDefinitoin();
                    bd.setBeanClass(aClass);
                    bd.setLazy(aClass.getAnnotation(Lazy.class)!=null);
                    bd.setPrimary(aClass.getAnnotation(Primary.class)!=null);
                    //构建bean name
                    //getSimpleName() 类名 getName 类路径
                    String name = aClass.getSimpleName();
                    //默认首字母小写
                    name = name.substring(0,1).toLowerCase() + name.substring(1);
                    if(service!=null&&service.value().isBlank()==false){
                        name = service.value();
                    }else if (controller!=null&&controller.value().isBlank()==false){
                        name = controller.value();
                    }else if (repository!=null&&repository.value().isBlank()==false){
                        name = repository.value();
                    }else if (component!=null&&component.value().isBlank()==false){
                        name = component.value();
                    }

                    beanDefinitoinMap.put(name,bd);

                }

            }
        }
//        beanDefinitoinMap.forEach((k,v)->{
//            System.out.println(String.format("%s : %s",k,v));
//        });

        //根据存入beanDefinitoinMap 创建 bean 存入 singletonObject
        for (Map.Entry<String, BeanDefinitoin> entry : beanDefinitoinMap.entrySet()) {
            String name= entry.getKey();
            BeanDefinitoin beanDefinitoin = entry.getValue();
            if(beanDefinitoin.isLazy()){
                continue;
            }
            Object bean = null;
            if(beanDefinitoin.getBeanMethod()!=null){
                beanDefinitoin.getBeanMethod().setAccessible(true);
                if (beanDefinitoin.getBeanMethod().getParameterCount()==0) {
                    //只执行无参数bean方法，有参数测bean方法涉及 自动装载
                    bean = beanDefinitoin.getBeanMethod().invoke(beanDefinitoin.getBeanConfig());
                }

            }else{
                Object beanClass = beanDefinitoin.getBeanClass();
                if (beanClass instanceof  Class){
                    Class cls = (Class) beanClass;
                    bean = cls.newInstance();
                }
            }
            if (bean!=null){
                singletonObject.put(name,bean);
            }
        }
        singletonObject.forEach((name,bean)->{
            System.out.println(String.format("%s : %s",name,bean));
        });

    }
    //搜索类文件
    public void scanClassFile(File file, List<File> files){
        for (File f : file.listFiles()) {
            if (f.isDirectory()) {
                scanClassFile(f,files);
            }else if(f.isFile()){
                if (f.getName().endsWith(".class")) {
                    files.add(f);
                }
            }else {

            }
        }
    }
    //实现getBean方法

    public  <T> T getBean(Class<T> cls) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        //查找 该类型所有的beanDefinitoin，确认那个是primary 主bean
        List<Map.Entry<String, BeanDefinitoin>> list = beanDefinitoinMap
                .entrySet()
                .stream()
                .filter(entry -> {
            BeanDefinitoin beanDefinitoin = entry.getValue();
            Object beanClass = beanDefinitoin.getBeanClass();
            Class clazz = (Class) beanClass;
            if (clazz.isAssignableFrom(cls)) {
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        if(list.isEmpty()){
            throw new RuntimeException("没有该bean："+cls);
        }else if (list.size()==1){
            Map.Entry<String, BeanDefinitoin> entry = list.get(0);
            String name = entry.getKey();
            BeanDefinitoin beanDefinitoin = entry.getValue();
            if(beanDefinitoin.isLazy()){
                //TODO 实现懒加载创建bean
               Object bean = lazyLoad(cls, name);
               return (T) bean;
            }else {
                return (T)singletonObject.get(name);
            }
        }else {
            List<Map.Entry<String, BeanDefinitoin>> list1 = list.stream().filter(entry -> {
                BeanDefinitoin beanDefinitoin = entry.getValue();
                if (beanDefinitoin.isPrimary()) {
                    return true;
                }
                return false;
            }).collect(Collectors.toList());
            if (list.isEmpty()){
                throw new RuntimeException("没有主bean"+cls);
            }else if (list1.size()==1) {
                Map.Entry<String, BeanDefinitoin> entry = list.get(0);
                String name = entry.getKey();
                BeanDefinitoin beanDefinitoin = entry.getValue();
                if(beanDefinitoin.isLazy()){
                    //TODO 实现懒加载创建bean
                    Object bean = lazyLoad(cls, name);
                    return (T) bean;
                }else {
                    return (T)singletonObject.get(name);
                }
            }else {
                throw new RuntimeException("包含有多个主bean"+cls);
            }
        }
    }

    public Object getBean(String id ) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        Object bean = singletonObject.get(id);
        if(bean!=null){
            return bean;
        }else {
            //懒加载
            BeanDefinitoin beanDefinitoin = beanDefinitoinMap.get(id);
            if (beanDefinitoin == null){
                throw new RuntimeException("没有该bean："+id);
            }
            // TODO 实现懒加载创建bean
            bean = lazyLoad(id, beanDefinitoin);
        }
        return bean;
    }

    public Object lazyLoad(String name,BeanDefinitoin beanDefinitoin) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        Object bean = null;
        bean = beanDefinitoin.getBeanMethod().invoke(beanDefinitoin.getBeanConfig());

        if (bean!=null){
            singletonObject.put(name,bean);
            return bean;
        }
      throw new RuntimeException("没有该bean:"+name);
    }

    public <T> T lazyLoad(Class<T> cls,String name) throws InvocationTargetException, IllegalAccessException, InstantiationException {
        Object bean = null;
        System.out.println(cls);
        bean = cls.newInstance();
        if (bean!=null){
            singletonObject.put(name,bean);
            return (T) bean;
        }
        throw new RuntimeException("没有该bean:"+cls);
    }

    public static void main(String[] args) throws UnsupportedEncodingException, ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException {
        MyAnnotaltionConfigApplicationContext cxt = new MyAnnotaltionConfigApplicationContext(AnnoConfig.class);
        System.out.println("#############################");
        UserMapper userMapper = cxt.getBean(UserMapper.class);
        System.out.println("userMapper = " + userMapper);
        Object userBiz = cxt.getBean("userBiz");
        System.out.println("userBiz = " + userBiz);
        Object userControllerAuto = cxt.getBean("userControllerAuto");
        System.out.println("userControllerAuto = " + userControllerAuto);

        Object person = cxt.getBean("person");
        System.out.println("person = " + person);

        Object person1 = cxt.getBean("person1");
        System.out.println("person1 = " + person1);

        Object userControllerxxxxAuto = cxt.getBean("userControllerxxxxAuto");
        System.out.println("userControllerxxxxAuto = " + userControllerxxxxAuto);

        //TODO 扩展自动加载 AUTO
    }

}




@Data
class BeanDefinitoin{
    private Object beanClass;   //bean类型
    private Method beanMethod;  //bean方法
    private Object beanConfig;  //配置类对象
    private boolean primary;    //主bean
    private boolean lazy;       //懒加载
}
